#include "devinsight.h"
#include "access/ustore/undo/knl_uundozone.h"




DVI_DEF_GDB_FUNCTION(dviUHeapTuple, "解析打印一个ustore HeapTuple的元组内容, 需要tupledesc， null好像有点问题目前。")
const char* dviUHeapTuple(UHeapTuple tup, TupleDesc desc)
{
    if (tup == NULL) {
        return "wrong input.";
    }

    StringInfoData res;
    initStringInfo(&res);

    /* tuple header */
    UHeapDiskTuple dtup = tup->disk_tuple;
    appendStringInfo(&res, "Tuple Header:\n");
    appendStringInfo(&res, "\txid: %u\n", dtup->xid);
    appendStringInfo(&res, "\ttd:%d\n", dtup->td_id);
    appendStringInfo(&res, "\tlocker_td:%d\n", dtup->reserved);
    appendStringInfo(&res, "\tcolumn_count:%d\n", UHeapTupleHeaderGetNatts(dtup));
    appendStringInfo(&res, "\tt_hoff:%d\n", dtup->t_hoff);
    appendStringInfo(&res, "\tflag:%d/%d [%s%s%s%s%s%s%s%s%s%s%s]\n", 
        dtup->flag, dtup->flag2,
        ((dtup->flag & UHEAP_HAS_NULL) ? "UHEAP_HASNULL " : ""),
        ((dtup->flag & UHEAP_DELETED) ? "UHEAP_DELETED " : ""),
        ((dtup->flag & UHEAP_INPLACE_UPDATED) ? "UHEAP_INPLACE_UPDATED " : ""),
        ((dtup->flag & UHEAP_UPDATED) ? "UHEAP_UPDATED " : ""),
        ((dtup->flag & UHEAP_XID_KEYSHR_LOCK) ? "UHEAP_XID_KEYSHR_LOCK " : ""),
        ((dtup->flag & UHEAP_XID_NOKEY_EXCL_LOCK) ? "UHEAP_XID_NOKEY_EXCL_LOCK " : ""),
        ((dtup->flag & UHEAP_XID_EXCL_LOCK) ? "UHEAP_XID_EXCL_LOCK " : ""),
        ((dtup->flag & UHEAP_MULTI_LOCKERS) ? "UHEAP_MULTI_LOCKERS " : ""),
        ((dtup->flag & UHEAP_INVALID_XACT_SLOT) ? "UHEAP_INVALID_XACT_SLOT " : ""),
        ((dtup->flag & SINGLE_LOCKER_XID_IS_LOCK) ? "SINGLE_LOCKER_XID_IS_LOCK " : ""),
        ((dtup->flag & SINGLE_LOCKER_XID_IS_SUBXACT) ? "SINGLE_LOCKER_XID_IS_SUBXACT " : "")
    );

    /* values */
    appendStringInfo(&res, "Column values:\n");
    if (desc == NULL) {
        appendStringInfo(&res, "\tno tupledesc, skip\n");
        return (const char*)res.data;
    }
    Datum* values = (Datum*)palloc0(sizeof(Datum) * desc->natts);
    bool* nulls = (bool*)palloc0(sizeof(bool) * desc->natts);
    UHeapDeformTuple(tup, desc, values, nulls);
    for (int i = 0; i < desc->natts; i++) {
        if (nulls[i]) {
            appendStringInfo(&res, "\t(null column)\n");
        }
        Form_pg_attribute adesc = &desc->attrs[i];
        HeapTuple typtup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(adesc->atttypid));
        Form_pg_type typetyp = (Form_pg_type)GETSTRUCT(typtup);
        regproc outfunc = typetyp->typoutput;
        ReleaseSysCache(typtup);

        char* str = DatumGetCString(OidFunctionCall1(outfunc, values[i]));
        appendStringInfo(&res, "\t%s\n", str);
    }
    pfree(values);
    pfree(nulls);
    
    return (const char*)res.data;
}



const char* dviUHeapTupleChain(UHeapTuple tup, TupleDesc desc)
{
    if (tup == NULL) {
        return "wrong input.";
    }

    StringInfoData res;
    initStringInfo(&res);


    return (const char*)res.data;
}

/*
 * 打印 Ustore Page 页面结构。
 * 临时表可能有点问题。凑活用
 */
DVI_DEF_GDB_FUNCTION(dviUPage, "解析打印一个ustore 页面的内容。传tupledesc可额外打印页面元组， null好像有点问题目前。")
const char* dviUPage(Page page, TupleDesc desc)
{
    if (page == NULL)
        return "null input page.";

    StringInfoData res;
    initStringInfo(&res);

    /* 1 HEADER */
    appendStringInfo(&res, "UPage Header:\n");
    UHeapPageHeaderData* phdr = (UHeapPageHeaderData*)page;
    appendStringInfo(&res, "\tpd_lsn:%u/%u\n", phdr->pd_lsn.xlogid, phdr->pd_lsn.xrecoff);
    appendStringInfo(&res, "\tpd_flags:%d\n", phdr->pd_flags);
    appendStringInfo(&res, "\tpd_lower and pd_upper: %d - %d = %d\n", phdr->pd_lower, phdr->pd_upper, (phdr->pd_upper - phdr->pd_lower));
    appendStringInfo(&res, "\tpotential_freespace:%d\n", phdr->potential_freespace);
    appendStringInfo(&res, "\ttd_count:%d\n", phdr->td_count);

    /* 2 TD */
    appendStringInfo(&res, "TD:\n");
    int tdCount = phdr->td_count;
    UHeapPageTDData* tdPtr = (UHeapPageTDData*)PageGetTDPointer(page);
    for (int i = 0; i < tdCount; i++) {
        TD* td = &tdPtr->td_info[i];
        // td id 实际上是从1开始算的，0用来表示forzen tdid。
        appendStringInfo(&res, "    %d| xactid:%lu, undo_record_ptr:%lu (%lu.%lu)\n", 
            i + 1, td->xactid, td->undo_record_ptr, td->undo_record_ptr >> 44, td->undo_record_ptr >> 44);
    }

    /* 3 TUPLE */
    UHeapTuple tuple = uheaptup_alloc(UHeapTupleDataSize);
    int nline = (phdr->pd_lower <= SizeOfUHeapPageHeaderData) ?
        0 : (phdr->pd_lower - (SizeOfUHeapPageHeaderData + SizeOfUHeapTDData(phdr))) / sizeof(RowPtr);
    appendStringInfo(&res, "TUPLE:\n");
    for (int i = FirstOffsetNumber; i <= nline; i++) {
        appendStringInfo(&res, "# %d|", i);
        RowPtr* rowptr = UPageGenerateRowPtr(page, i);
        if (!RowPtrIsUsed(rowptr)) {
            appendStringInfo(&res, "[UNUSED].\n");
        } else if (RowPtrIsDead(rowptr)) {
            appendStringInfo(&res, "[DEAD length %u offset %u]\n", RowPtrGetLen(rowptr), RowPtrGetOffset(rowptr));
        } else if (RowPtrIsNormal(rowptr)) {
            appendStringInfo(&res, "[NORMAL length %u offset %u]\n", RowPtrGetLen(rowptr), RowPtrGetOffset(rowptr));
            if (desc != NULL) {
                tuple->disk_tuple = (UHeapDiskTuple)UPageGetRowData(page, rowptr);
                tuple->disk_tuple_size = RowPtrGetLen(rowptr);
                appendStringInfo(&res, ">>>>>>>>>>tuple detail<<<<<<<<<<\n%s\n", dviUHeapTuple(tuple, desc));
            }
        } else {
            appendStringInfo(&res, "[REDIRECTED length %u offset %u]\n", RowPtrGetLen(rowptr), RowPtrGetOffset(rowptr));
        }
     }
    return (const char*)res.data;
}




/********************************************************************************************
 * UndoRecord
 ********************************************************************************************/
/* 强行解释内容 */
typedef struct UndoRecordParser {
    StringInfoData rawdata_;
    int index_;
    UndoRecordHeader whdr_;
    UndoRecordBlock wblk_;
    UndoRecordTransaction wtxn_;
    UndoRecordPayload wpay_;
    UndoRecordOldTd wtd_;
    UndoRecordPartition wpart_;
    UndoRecordTablespace wtspc_;

    UndoRecPtr urp_; /* undo record pointer */
    Buffer buff_;    /* buffer in which undo record data points */
    int bufidx_;

    bool needInsert_;
    bool isCopy_;
    MemoryContext mem_context_;
} UndoRecordParser;


DVI_DEF_GDB_FUNCTION(dviUndoRecord, "解析打印一个UndoRecord。")
const char* dviUndoRecord(UndoRecord* urec)
{
    static const char* utype_str[] = {
        "UNDO_UNKNOWN", "UNDO_INSERT", "UNDO_MULTI_INSERT", "UNDO_DELETE", "UNDO_INPLACE_UPDATE",
        "UNDO_UPDATE",  "?",           "?",                 "?",           "UNDO_ITEMID_UNUSED"};
    if (urec == NULL) {
        return "NULL input error. ";
    }

    UndoRecordParser* urecp = (UndoRecordParser*)urec;
    StringInfoData res;
    initStringInfo(&res);

    appendStringInfo(&res, "Class var:\n");
    appendStringInfo(&res, "\tindex_:%d\n", urecp->index_);
    appendStringInfo(&res, "\turp:%lu\n", urecp->urp_);
    appendStringInfo(&res, "\tbuff:%d\n", urecp->buff_);
    appendStringInfo(&res, "\tbufidx:%d\n", urecp->bufidx_);
    appendStringInfo(&res, "\tneedInsert:%s\n", urecp->needInsert_ ? "true" : "false");
    appendStringInfo(&res, "\tisCopy:%s\n", urecp->isCopy_ ? "true" : "false");


    appendStringInfo(&res, "Header:\n");
    appendStringInfo(&res, "\txid:%lu\n", urecp->whdr_.xid);
    appendStringInfo(&res, "\tcid:%u\n", urecp->whdr_.cid);
    appendStringInfo(&res, "\treloid:%u\n", urecp->whdr_.reloid);
    appendStringInfo(&res, "\trelfilenode:%u\n", urecp->whdr_.relfilenode);
    appendStringInfo(&res, "\tutype:%u (%s)\n", urecp->whdr_.utype, utype_str[urecp->whdr_.utype]);
    appendStringInfo(&res, "\tuinfo:%u\n", urecp->whdr_.uinfo);


    if (urecp->whdr_.uinfo == UNDO_UREC_INFO_UNKNOWN) {
        appendStringInfo(&res, "Unknown.\n");
    }
    if (urecp->whdr_.uinfo & UNDO_UREC_INFO_PAYLOAD) {
        appendStringInfo(&res, "Payload\n");
        appendStringInfo(&res, "\tpayloadlen: %d\n", urecp->wpay_.payloadlen);
    }
    if (urecp->whdr_.uinfo & UNDO_UREC_INFO_TRANSAC) {
        appendStringInfo(&res, "Transac:\n");
        appendStringInfo(&res, "\tprev urp: %lu\n", urecp->wtxn_.prevurp);
    }
    if (urecp->whdr_.uinfo & UNDO_UREC_INFO_BLOCK) {
        appendStringInfo(&res, "Block:\n");
        appendStringInfo(&res, "\tblkprev: %lu\n", urecp->wblk_.blkprev);
        appendStringInfo(&res, "\tblkno: %u\n", urecp->wblk_.blkno);
        appendStringInfo(&res, "\toffset: %u\n", urecp->wblk_.offset);
    }
    if (urecp->whdr_.uinfo & UNDO_UREC_INFO_OLDTD) {
        appendStringInfo(&res, "Old td:\n");
        appendStringInfo(&res, "\told xactid: %lu\n", urecp->wtd_.oldxactid);
    }
    if (urecp->whdr_.uinfo & UNDO_UREC_INFO_CONTAINS_SUBXACT) {
        appendStringInfo(&res, "Subxact:\n");
        SubTransactionId *subxid = (SubTransactionId *)((char *)(urecp->rawdata_.data) + (urecp->rawdata_.len - sizeof(SubTransactionId)));
        appendStringInfo(&res, "\tflag_subxact, subxid = %lu.\n", *subxid);
    }
    if (urecp->whdr_.uinfo & UNDO_UREC_INFO_HAS_PARTOID) {
        appendStringInfo(&res, "Partition:\n");
        appendStringInfo(&res, "\tpartitionoid: %u\n", urecp->wpart_.partitionoid);
    }
    if (urecp->whdr_.uinfo & UNDO_UREC_INFO_HAS_TABLESPACEOID) {
        appendStringInfo(&res, "Tablespace:\n");
        appendStringInfo(&res, "\ttablespace: %u\n", urecp->wtspc_.tablespace);
    }

    return (const char*)res.data;
}

DVI_DEF_GDB_FUNCTION(dviUndoRecord, "根据UndoRecPtr，找到UndoRecord并解析打印")
const char* dviUndoRecord(UndoRecPtr urp)
{
    if (urp == 0) {
        return "invalid UndoRecPtr, 0.\n";
    }

    StringInfoData res;
    initStringInfo(&res);

    UndoRecord* urec = New(CurrentMemoryContext)UndoRecord();
    urec->SetUrp(urp);
    urec->SetMemoryContext(CurrentMemoryContext);
    urec->Load(false);

    return dviUndoRecord(urec);
}




/********************************************************************************************
 * UndoZone
 ********************************************************************************************/
DVI_DEF_GDB_FUNCTION(dviUndoZone, "根据zid，找到UndoZone并解析打印")
const char* dviUndoZone(int zid)
{
    StringInfoData res;
    initStringInfo(&res);

    return (const char*)res.data;
}

DVI_DEF_GDB_FUNCTION(dviUndoZone, "解析打印一个UndoZone")
const char* dviUndoZone(undo::UndoZone* uzone)
{
    StringInfoData res;
    initStringInfo(&res);

    return (const char*)res.data;
}
